{ Esta unidad se encargar de contener la clase y los objetos que forman la solución.
 Una solución está compuesta por una lista de proyectos, los cuales a su vez contienen
 una lista de ficheros.

  Los procedimientos que guardan datos los hacen  formato XML, el código
  original es de aquí: http://wiki.lazarus.freepascal.org/XML_Tutorial

  Copyright (C) 2010 Jorge Turiel jorgeturiel@gmail.com

  This source is free software; you can redistribute it and/or modify it under
  the terms of the GNU General Public License as published by the Free
  Software Foundation; either version 2 of the License, or (at your option)
  any later version.

  This code is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  details.

  A copy of the GNU General Public License is available on the World Wide Web
  at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
  to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
  MA 02111-1307, USA.
}

unit uSolucion;

{$mode objfpc}{$H+}

interface
uses
  Classes, SysUtils,DOM, XMLWrite,XMLRead,Strings,uBoards;

Type TFichero = Record
     Nombre : String; //Nombre del fichero
     idTab  : Integer; //Tag, del Tabsheet que lo contiene, -1 sino tiene ninguno
     Compilar : boolean; //indica si el fichero es compilable o no
end;

Type

{ TProyecto }

TProyecto = class//(TObject)
private

     Public
           Nombre : String;
           Ruta : String; //Contiene la ruta dónde se guarda el fichero del proyecto
           Modificado : Boolean;
           Ficheros : Array of TFichero;
           Placa : String; //Este proyecto es para este tipo de placa.
           Constructor Create (stNombre:String;stFichero:String;nTab : Integer);//override;
           Procedure BorrarFichero (Indice : Integer);
           Function AddFichero (Fichero : TFichero):boolean;  //Añade un fichero
           Procedure Guardar (stRuta : String);
           Procedure Cargar (stRuta : String);
           Function Contar: Integer;
           Procedure GuardarFichero (aFichero : TFichero; stRuta : String);

end;
Type

{ TSolucion }

TSolucion = class// (TObject)
     Public
     Nombre : string;
     Ruta : String; //Ruta dónde se guarda esta solución
     Proyectos : Array of TProyecto;
     Modificado : Boolean;
     Constructor Create (Proyecto : TProyecto);
     Function AddProyecto (Proyecto : TProyecto):Boolean;
//     Procedure CargaProyecto (
     Procedure Guardar (stRuta : String);
     Procedure Cargar  (stRuta : String);
     Procedure BorrarProyecto (Indice : Integer);
     Function Contar:Integer;
     Function ExisteProyecto (aNombre : String):Boolean;

end;
implementation
Uses uEditorComun;
{TSolucion}
constructor TSolucion.Create (Proyecto: TProyecto );
Begin
   //Borrar;
   Nombre := Proyecto.Nombre;
   SetLength (Proyectos,0);
   AddProyecto (Proyecto);
   If (Proyecto.Ruta <> '') and (Not (DirectoryExists (Proyecto.ruta)))Then
   Begin
    CreateDir (Proyecto.ruta)
   end;
end;
//No puede haber dos proyectos iguales
Function TSolucion.AddProyecto (Proyecto: TProyecto ):Boolean;
Var
    Dimension :integer;
    I : Integer;
    Existe : Boolean;
    Registro : TProyecto;

begin
 Result := False;
 I := 0;
 Existe := False;
 Registro := TProyecto.Create('','',-1);
 While I < Length (Proyectos) do
 Begin
     Registro := Proyectos[i];
     If StriComp (pchar(Registro.Nombre),pchar(Proyecto.Nombre)) = 0 Then
     Begin
      Existe := True;
      I := Length (Proyectos);
     end;
     Inc(I);
 end;

 If Not Existe Then
 Begin
  Dimension := Length (Proyectos);
  SetLength (Proyectos,Dimension+1);
  Proyectos [Dimension] := Proyecto;
  Modificado := True;
  Result := True;
 end;
end;
//Todo: Añadir tipo de Arduino cuando esté en el TProyecto
//Todo: Debe Comprobar si hay algún proyecto pendiente de guardar
//Una posibilidad es crear una rutina que devuelva el primer fichero
//Que encuentre que se debe modificar, y este procedimiento hacerlo privado
//Guardar el nombre del proyecto y la ruta dónde está guardado.
procedure TSolucion.Guardar (stRuta: String );
Var
    I : Integer;
    xDoc : TXMLDocument;
    RootNode,
    ParentNode: TDOMELement;
    Registro : TProyecto;
    NodoHijo: TDOMText;
begin
  //Crear Documento XML
  xDoc := TXMLDocument.create;
  //Crear nodo Raiz
  RootNode := xDoc.CreateElement ('Solucion');
  xdoc.AppendChild(RootNode);
  For I:= 0 To Length (Proyectos)-1 DO
  Begin
      Registro := Proyectos[i];
      RootNode := xDoc.DocumentElement;
      ParentNode := xDoc.CreateElement('Proyecto');
      TDOMElement(ParentNode).SetAttribute('ID',IntToStr(i));
      RootNode.AppendChild(ParentNode);
      //Crear un nodo hijo del Nodo que acabo de crear (Nodo: RootNode);
      ParentNode := xDoc.CreateElement('Nombre');
      NodoHijo := xDoc.CreateTextNode(Registro.Nombre);
      ParentNode.AppendChild(NodoHijo);
      RootNode.ChildNodes.Item[I].AppendChild(ParentNode);

      {  ParentNode := xdoc.CreateElement('Nombre');
       NodoHijo := xdoc.CreateTextNode(Registro.Nombre);
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[i].AppendChild(ParentNode); }
      //Crear Otro hijo
      ParentNode := xDoc.CreateElement('Ruta');
      NodoHijo := xDoc.CreateTextNode(Registro.Ruta);
      ParentNode.AppendChild(NodoHijo);
      RootNode.ChildNodes.Item[I].AppendChild(ParentNode);
  end;

   //Guardar Fichero XML
   WriteXMLFile (xdoc,stRuta);
   Modificado := False;
   xdoc.free;
end;
 { TODO : Pendiente de Probar }
procedure TSolucion.Cargar (stRuta: String );
Var
    I : Integer;
    NodoPadre : TDOMNode;
    ProyectoACargar : TProyecto;
    xDoc : TXMLDocument;
    NombreProyecto,
    RutaProyecto : string;
begin
  ReadXMLFile(xdoc,stRuta);
  SetLength (Proyectos,0);
  For I:= 0 to xdoc.DocumentElement.ChildNodes.Count-1 do
  Begin
      NodoPadre := xDoc.DocumentElement.ChildNodes[i];
      NombreProyecto:=NodoPadre.ChildNodes[0].TextContent;
      RutaProyecto:=NodoPAdre.ChildNodes[1].TextContent;
      RutaProyecto := RutaProyecto+NombreProyecto;
      ProyectoACargar := TProyecto.Create('','',-1);
      ProyectoACargar.Cargar(RutaProyecto);
      AddProyecto (ProyectoACargar);
  end;
  Ruta := ExtractFilePath (stRuta);

  If ProyectoACargar <> nil Then
     FreeAndNil(ProyectoACargar);
  xdoc.free;

end;

procedure TSolucion.BorrarProyecto (Indice: Integer );
Var
   ALength : Integer;
   TailElements : Cardinal;
Begin
   ALength := Length(Proyectos);
   Assert(ALength > 0);
   Assert(Indice < ALength);
   Finalize(Proyectos[Indice]);
   TailElements := ALength - Indice;
   if TailElements > 0 then
   Move(Proyectos[Indice + 1], Proyectos[Indice], SizeOf(TProyecto) * TailElements);
   Initialize(Proyectos[ALength - 1]);
   SetLength(Proyectos, ALength - 1);
   Modificado := True;
end;



function TSolucion.Contar: Integer;
begin
  Result := Length (Proyectos);
end;

function TSolucion.ExisteProyecto(aNombre: String): Boolean;
var
  I: Integer;
  NombreActual: String;
begin
  I:= 0;
  Result := False;
  While I < Length (Proyectos) do
  Begin
   NombreActual := Proyectos [i].Nombre;
   If aNombre = NombreActual Then
   Begin
    Result := True;
    I := Length (Proyectos);
   end;
   Inc(i);
  end;
end;



{TProyecto}
//Todo: Añadir tipo de Arduino cuando esté en el TProyecto

constructor TProyecto.Create(stNombre: String; stFichero: String; nTab: Integer);
Var
  FicheroNuevo : TFichero;
Begin
  SetLength (Ficheros,1);
  FicheroNuevo.Nombre:=stFichero;
  FicheroNuevo.Compilar:= True;
  FicheroNuevo.idTab:= nTab;
  Nombre := stNombre;
  Ruta := '';
  Modificado := True;
  Ficheros[0] := FicheroNuevo;
end;

//Borra un fichero de la lista
//Original de: http://pages.cs.wisc.edu/~rkennedy/array-delete

Procedure TProyecto.BorrarFichero (Indice : Integer);
Var
   ALength : Integer;
   TailElements : Cardinal;
Begin

   ALength := Length(Ficheros);
   Assert(ALength > 0);
   Assert(Indice < ALength);
   Finalize(Ficheros[Indice]);
   TailElements := ALength - Indice;
   if TailElements > 0 then
   Move(Ficheros[Indice + 1], Ficheros[Indice], SizeOf(TFichero) * TailElements);
   Initialize(Ficheros[ALength - 1]);
   SetLength(Ficheros, ALength - 1);
   Modificado := True;
end;
//No puede haber dos ficheros con el mismo nombre
Function TProyecto.AddFichero (Fichero: TFichero ):Boolean;
Var
  Dimension : Integer;
  I : Integer;
  Existe : Boolean;
  Registro: TFichero;

begin
   Result := False;
   Existe := False;
   I:= 0;
   While I < Length (Ficheros) Do
   Begin
       Registro := Ficheros[i];
       If StrIComp (pChar(Registro.Nombre),pChar(Fichero.Nombre)) = 0 then
       begin
        Existe := true;
        i := Length (Ficheros);
       end;

   end;

   If Not Existe Then
   Begin
    Dimension := Length (Ficheros);
    SetLength (Ficheros,Dimension+1);
    Ficheros[Dimension] := Fichero;
    Modificado := True;
    Result := True;
   end;
end;
//Todo: Debe Comprobar si hay algún Fichero pendiente de guardar.
//Una posibilidad es crear una rutina que devuelva el primer fichero
//Que encuentre que se debe modificar, y este procedimiento hacerlo privado
Procedure TProyecto.Guardar (stRuta : String);
var
  xdoc: TXMLDocument;
  RootNode: TDOMElement;
  NodoHijo: TDOMText;
  ParentNode: TDOMElement;
  I: Integer;
  Registro: TFichero;
  FicheroActual: TFichero;
Begin
   //Crear documento XMl
   xdoc := TXMLDocument.create;
   //Crear nodo raiz.
   RootNode := xdoc.CreateElement('Proyecto');
   xdoc.AppendChild(RootNode);
   For I:= 0 To Length (Ficheros)-1 Do
   Begin
       Registro := Ficheros[i];
       //Crear un nodo padre
       RootNode := xdoc.DocumentElement;
       ParentNode := xdoc.CreateElement('Fichero');
       TDOMElement(ParentNode).SetAttribute('ID',IntToStr (I));
       RootNode.AppendChild(ParentNode);

       //Crear un nodo hijo
       ParentNode := xdoc.CreateElement('Nombre');
       NodoHijo := xdoc.CreateTextNode(Registro.Nombre);
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[i].AppendChild(ParentNode);

       //Crear Hijo
       ParentNode := xDoc.CreateElement('Idtab');
       NodoHijo := xdoc.CreateTextNode(IntToStr (Registro.idTab));
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[i].AppendChild(ParentNode);

       //Crear Hijo
       ParentNode := xDoc.CreateElement('Compilar');
       NodoHijo := xdoc.CreateTextNode(BoolToStr (Registro.Compilar));
       ParentNode.AppendChild(NodoHijo);
       RootNode.ChildNodes.Item[i].AppendChild(ParentNode);
   end;

   //Guardar Fichero XML
   WriteXMLFile (xdoc,stRuta);
   Modificado := False;
   Ruta := ExtractFilePath (stRuta);
   xdoc.free;
   //Guarda Ficheros del proyecto
   For I:= 0 to Length (Ficheros)-1 do
   Begin
       FicheroActual := Ficheros[i];
       GuardarFichero (FicheroActual,stRuta);
   end;
end;

procedure TProyecto.Cargar (stRuta: String );
Var
   xdoc : TXMLDocument;
   I: Integer;
   NodoPadre: TDOMNode;
   Registro: TFichero;
begin
 ReadXMLFile (xdoc,stRuta);
 SetLength (Ficheros,0);

 Nombre := ExtractFilePath (stRuta);
 For I:= 0 To xdoc.DocumentElement.ChildNodes.Count-1 do
 Begin
     NodoPadre := xdoc.DocumentElement.ChildNodes[i];
     Registro.Nombre:= NodoPadre.ChildNodes[0].TextContent;
     Registro.idTab:= StrToInt (NodoPadre.ChildNodes[1].TextContent);
     Registro.Compilar:=StrToBool(NodoPadre.ChildNodes[2].TextContent);
     AddFichero (Registro);
 end;
 Modificado := False;
 xdoc.free;
end;

function TProyecto.Contar: Integer;
begin
  Result := Length (Ficheros);
end;

procedure TProyecto.GuardarFichero(aFichero: TFichero; stRuta: String);
begin
  If aFichero.idTab < 1 Then Exit;
  //Localizar la pestaña
  GuardarEditor (aFichero.idTab,stRuta+PathDelim+aFichero.Nombre);
end;

end.

